home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Memory / MemPtchM.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-28  |  16.5 KB  |  564 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        MemPtchM.cpp
  3.  
  4.     Contains:    Code to make MacOS memory routines call this memory manager
  5.  
  6.     Owned by:    Jens Alfke (based on code by Troy Gaul)
  7.  
  8.     Copyright:    © 1995 - 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <1>     6/19/96    jpa        first checked in
  13.     
  14.     In Progress:
  15.     
  16. */
  17.  
  18.  
  19. #include "MemMgr.h"
  20. #include "PlatfMem.h"
  21.  
  22. #include <CodeFragments.h>
  23. #include <MixedMode.h>
  24. #include <Traps.h>
  25.  
  26.  
  27. //==============================================================================
  28. //    Debugging support
  29. //==============================================================================
  30.  
  31. #define WARN_ON_PATCH_CALL 0    // Set to 1 to break on every patch call
  32.  
  33. #define PATCH_WARN    \
  34.     if(!MM_DEBUG || !WARN_ON_PATCH_CALL) ; else MM_SHOW_WARNING
  35.  
  36.  
  37. //==============================================================================
  38. //    Constants / Types
  39. //==============================================================================
  40.  
  41. const Size   kSizeThreshold = 0;            // Min block size to allocate with MMM
  42. const OSType kTag           = 'ø∫pt';        // Magic tag at start of block
  43.  
  44. // Typedef of private SetEmulatorRegister SPI call:
  45. typedef void (*SetEmulatorRegisterProcPtr) ( registerSelectorType, long newValue );
  46.  
  47.  
  48. //==============================================================================
  49. //    Structs
  50. //==============================================================================
  51.  
  52. struct BlockHeader {
  53.     Size    size;        // Logical size of block (MMM only knows physical size)
  54.     OSType    tag;        // Magic tag (kTag) to detect if this is a block I allocated
  55. };
  56.  
  57. #define HEADER_OF_BLOCK(B)    ((BlockHeader*)((char*)(B)-sizeof(BlockHeader)))
  58. #define BLOCK_OF_HEADER(H)    ((void*)((char*)(H)+sizeof(BlockHeader)))
  59.  
  60.  
  61. //——————————————————————————————————————————————————————————————————————————————
  62. //    Global Variables
  63. //——————————————————————————————————————————————————————————————————————————————
  64.  
  65. static MemHeap* gOSHeap = kMMNULL;
  66.  
  67. static long        gOverridePtrs = 0;
  68. static long        gOverrideHandles = 0;
  69.  
  70. #if GENERATINGPOWERPC
  71. SetEmulatorRegisterProcPtr gSetEmulatorRegisterAddr = kMMNULL;
  72. #endif
  73.  
  74. static UniversalProcPtr gNewHandleAddress  = NULL;
  75. static UniversalProcPtr gNewPtrAddress     = NULL;
  76. static UniversalProcPtr gDisposePtrAddress = NULL;
  77. static UniversalProcPtr gGetPtrSizeAddress = NULL;
  78. static UniversalProcPtr gSetPtrSizeAddress = NULL;
  79. static UniversalProcPtr gMaxBlockAddress   = NULL;
  80. static UniversalProcPtr gPtrZoneAddress    = NULL;
  81.  
  82. #if MM_DEBUG
  83. long gNewPtrBlockCount = 0;
  84. #endif
  85.  
  86.  
  87. //——————————————————————————————————————————————————————————————————————————————
  88. //    ProcInfos
  89. //——————————————————————————————————————————————————————————————————————————————
  90.  
  91. enum
  92. {
  93.     uppNewHandleProcInfo
  94.         = kRegisterBased
  95.             | RESULT_SIZE(SIZE_CODE(sizeof(Handle)))
  96.             | REGISTER_RESULT_LOCATION(kRegisterA0)
  97.             | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(unsigned short)))
  98.             | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(Size)))
  99.     ,
  100.     uppNewPtrProcInfo
  101.         = kRegisterBased
  102.             | RESULT_SIZE(SIZE_CODE(sizeof(Ptr)))
  103.             | REGISTER_RESULT_LOCATION(kRegisterA0)
  104.             | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(unsigned short)))
  105.             | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(Size)))
  106.     ,
  107.     uppDisposePtrProcInfo
  108.         = kRegisterBased
  109.             | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
  110.             | REGISTER_RESULT_LOCATION(kRegisterD0)
  111.             | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(unsigned short)))
  112.             | REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Ptr)))
  113.     ,
  114.     uppGetPtrSizeProcInfo
  115.         = kRegisterBased
  116.             | RESULT_SIZE(SIZE_CODE(sizeof(Size)))
  117.             | REGISTER_RESULT_LOCATION(kRegisterD0)
  118.             | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(unsigned short)))
  119.             | REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Ptr)))
  120.     ,
  121.     uppSetPtrSizeProcInfo
  122.         = kRegisterBased
  123.             | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
  124.             | REGISTER_RESULT_LOCATION(kRegisterD0)
  125.             | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(unsigned short)))
  126.             | REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Ptr)))
  127.             | REGISTER_ROUTINE_PARAMETER(3, kRegisterD0, SIZE_CODE(sizeof(Size)))
  128.     ,
  129.     uppMaxBlockProcInfo
  130.         = kRegisterBased
  131.             | RESULT_SIZE(SIZE_CODE(sizeof(long)))
  132.             | REGISTER_RESULT_LOCATION(kRegisterD0)
  133.             | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(unsigned short)))
  134.     ,
  135.     uppPtrZoneProcInfo
  136.         = kRegisterBased
  137.             | RESULT_SIZE(SIZE_CODE(sizeof(THz)))
  138.             | REGISTER_RESULT_LOCATION(kRegisterA0)
  139.             | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(unsigned short)))
  140.             | REGISTER_ROUTINE_PARAMETER(2, kRegisterA0, SIZE_CODE(sizeof(Ptr)))
  141. };
  142.  
  143.  
  144. //——————————————————————————————————————————————————————————————————————————————
  145. //    FindEmulatorCall
  146. //——————————————————————————————————————————————————————————————————————————————
  147.  
  148. // From MixedModePriv.h:
  149. // extern long SetEmulatorRegister(registerSelectorType registerSelector, long newValue);
  150.  
  151. #if GENERATINGPOWERPC
  152. static OSErr
  153. FindEmulatorCall( )
  154. {
  155.     CFragConnectionID conn;
  156.     Ptr mainAddr;
  157.     Str255 errName;
  158.     OSErr err;
  159.     err = GetSharedLibrary("\pPrivateInterfaceLib",kCurrentCFragArch,kLoadLib,
  160.                             &conn,&mainAddr,errName);
  161.     if( err ) return err;
  162.     
  163.     SymClass symClass;
  164.     err= FindSymbol(conn,"\pSetEmulatorRegister",(Ptr*)&gSetEmulatorRegisterAddr,&symClass);
  165.     
  166.     CloseConnection(&conn);
  167.     return err;
  168. }
  169. #endif
  170.  
  171.  
  172. //——————————————————————————————————————————————————————————————————————————————
  173. //    SetStatus
  174. //——————————————————————————————————————————————————————————————————————————————
  175. static void
  176. SetStatus( OSErr status )
  177. {
  178.     // Used by NewHandle, NewPtr and PtrZone, which return error codes in D0.
  179.     LMSetMemErr(status);
  180. #if GENERATINGPOWERPC
  181.     (*gSetEmulatorRegisterAddr)(kRegisterD0,status);
  182. #endif
  183. }
  184.  
  185.  
  186. //——————————————————————————————————————————————————————————————————————————————
  187. //    NewHandlePatch
  188. //——————————————————————————————————————————————————————————————————————————————
  189.  
  190. static pascal Handle
  191. NewHandlePatch(unsigned short trapWord, Size byteCount)
  192. {
  193.     Handle result;
  194.     if( gOverrideHandles>0
  195.             && (trapWord == _NewHandle || trapWord == _NewHandleClear)
  196.             && byteCount >= kSizeThreshold
  197.             && GetZone()==ApplicationZone() ) {
  198.  
  199.         long temp = gOverrideHandles;        // Temporarily disable to avoid infinite regress
  200.         gOverrideHandles = 0;
  201.         
  202.         result= (Handle) MMAllocateHandleIn(sizeof(BlockHeader) + byteCount, kMMTempMemory);
  203.  
  204.         gOverrideHandles = temp;
  205.     
  206.         if( result ) {
  207.             if( trapWord == _NewHandleClear )
  208.                 PlatformZapMem(*result,byteCount,0);
  209.             SetStatus(noErr);
  210.             PATCH_WARN("NewHandle(%ld) = %p",byteCount,result);
  211.             return result;
  212.         } else
  213.             MM_WARN("NewHandle(%ld) FAILED; trying app heap...",byteCount);
  214.         // If allocation in temp mem failed, try the app heap...
  215.     }
  216.     
  217.     result= (Handle)CallOSTrapUniversalProc(gNewHandleAddress, uppNewHandleProcInfo, 
  218.                                                trapWord, byteCount);
  219. #if GENERATINGPOWERPC
  220.     SetStatus(MemError());
  221. #endif
  222.  
  223.     return result;
  224. }
  225.  
  226.  
  227. //——————————————————————————————————————————————————————————————————————————————
  228. //    NewPtrPatch
  229. //——————————————————————————————————————————————————————————————————————————————
  230.  
  231. static pascal Ptr
  232. NewPtrPatch(unsigned short trapWord, Size byteCount)
  233. {
  234.     Ptr result;
  235.     if( gOverridePtrs>0
  236.             && (trapWord == _NewPtr || trapWord == _NewPtrClear)
  237.             && byteCount >= kSizeThreshold
  238.             && GetZone()==ApplicationZone() ) {
  239.  
  240.         BlockHeader *header;
  241.         if (trapWord == _NewPtr)
  242.             header = (BlockHeader*) MMAllocateIn(sizeof(BlockHeader) + byteCount, gOSHeap);
  243.         else
  244.             header = (BlockHeader*) MMAllocateClearIn(sizeof(BlockHeader) + byteCount, gOSHeap);
  245.  
  246.         if( header ) {
  247.             header->tag = kTag;
  248.             header->size = byteCount;
  249.             result = (Ptr) BLOCK_OF_HEADER(header);
  250.             #if MM_DEBUG
  251.             gNewPtrBlockCount++;
  252.             #endif
  253.             SetStatus(noErr);
  254.             PATCH_WARN("NewPtr(%ld) = %p",byteCount,result);
  255.             return result;
  256.         } else
  257.             MM_WARN("NewPtr(%ld) FAILED; trying app heap...",byteCount);
  258.         // If allocation in temp mem failed, try the app heap...
  259.     }
  260.         
  261.     result= (Ptr)CallOSTrapUniversalProc(gNewPtrAddress, uppNewPtrProcInfo, 
  262.                                                trapWord, byteCount);
  263. #if GENERATINGPOWERPC
  264.     SetStatus(MemError());
  265. #endif
  266.  
  267.     return result;
  268. }
  269.  
  270.  
  271. //——————————————————————————————————————————————————————————————————————————————
  272. //    DisposePtrPatch
  273. //——————————————————————————————————————————————————————————————————————————————
  274. static pascal OSErr
  275. DisposePtrPatch(unsigned short trapWord, Ptr p)
  276. {
  277.     BlockHeader *h = HEADER_OF_BLOCK(p);
  278.     if (p && h->tag == kTag)
  279.     {
  280.         PATCH_WARN("DisposePtr(%p);g",p);
  281.         h->tag = 0xDDDD;                // Minimizes chance this will look like a block later
  282.         MMFree(h);
  283.         LMSetMemErr(noErr);
  284.         #if MM_DEBUG
  285.         gNewPtrBlockCount--;
  286.         if( gNewPtrBlockCount==-1 ) MM_WARN("Too many NewPtr blocks disposed!?!?");
  287.         #endif
  288.         return noErr;
  289.     }
  290.     else
  291.     {
  292.         return CallOSTrapUniversalProc(gDisposePtrAddress, uppDisposePtrProcInfo, 
  293.                                 trapWord, p);
  294.     }
  295. }
  296.  
  297.  
  298. //——————————————————————————————————————————————————————————————————————————————
  299. //    GetPtrSizePatch
  300. //——————————————————————————————————————————————————————————————————————————————
  301. static pascal Size
  302. GetPtrSizePatch(unsigned short trapWord, Ptr p)
  303. {
  304.     BlockHeader *h = HEADER_OF_BLOCK(p);
  305.     if (p && h->tag == kTag)
  306.     {
  307.         PATCH_WARN("GetPtrSize(%p)",p);
  308.         LMSetMemErr(noErr);
  309.         return h->size;
  310.     }
  311.     else
  312.     {
  313.         return CallOSTrapUniversalProc(gGetPtrSizeAddress, uppGetPtrSizeProcInfo, 
  314.                                 trapWord, p);
  315.     }
  316. }
  317.  
  318.  
  319. //——————————————————————————————————————————————————————————————————————————————
  320. //    SetPtrSizePatch
  321. //——————————————————————————————————————————————————————————————————————————————
  322. static pascal OSErr
  323. SetPtrSizePatch(unsigned short trapWord, Ptr p, Size newSize)
  324. {
  325.     BlockHeader *h = HEADER_OF_BLOCK(p);
  326.     if (p && h->tag == kTag)
  327.     {
  328.         // Our memory manager lacks a way to grow a block into an adjacent free
  329.         // block. This is probably not a big deal. Grow it as much as it can:
  330.         PATCH_WARN("SetPtrSize(%p,%ld)",p,newSize);
  331.         OSErr err;
  332.         if( newSize <= MMBlockSize(h) ) {
  333.             h->size = newSize;
  334.             err= noErr;
  335.         } else
  336.             err= memFullErr;
  337.         LMSetMemErr(err);
  338.         return err;
  339.     }
  340.     else
  341.     {
  342.         return CallOSTrapUniversalProc(gSetPtrSizeAddress, uppSetPtrSizeProcInfo, 
  343.                                 trapWord, p, newSize);
  344.     }
  345. }
  346.  
  347.  
  348. //——————————————————————————————————————————————————————————————————————————————
  349. //    MaxBlockPatch
  350. //——————————————————————————————————————————————————————————————————————————————
  351. static pascal long 
  352. MaxBlockPatch( unsigned short trapWord )
  353. {
  354.     Size space = (Size) CallOSTrapUniversalProc(gMaxBlockAddress, uppMaxBlockProcInfo,
  355.                                 trapWord);
  356.     if( GetZone()==ApplicationZone() ) {
  357.         PATCH_WARN("MaxBlock()");
  358.  
  359.     Size unused;
  360.     Size tempSpace = TempMaxMem(&unused);
  361.         if( tempSpace > space )
  362.             space = tempSpace;
  363.     }
  364.     return space;
  365. }
  366.  
  367.  
  368. //——————————————————————————————————————————————————————————————————————————————
  369. //    PtrZonePatch
  370. //——————————————————————————————————————————————————————————————————————————————
  371. static pascal THz 
  372. PtrZonePatch(unsigned short trapword, Ptr p)
  373. {
  374.     BlockHeader *h = HEADER_OF_BLOCK(p);
  375.     if (p && h->tag == kTag)
  376.     {
  377.         // Not much we can do here but lie, since the actual zone is not a MacOS
  378.         // zone in any sense. But we know the block _would_ have been in the app zone:
  379.         PATCH_WARN("PtrZone(%p)",p);
  380.         return ApplicationZone();
  381.         SetStatus(noErr);
  382.     }
  383.     else
  384.     {
  385.         return (THz) CallOSTrapUniversalProc(gPtrZoneAddress, uppPtrZoneProcInfo, 
  386.                                 trapword, p);
  387.         SetStatus(MemError());
  388.     }
  389. }
  390.  
  391.  
  392. //——————————————————————————————————————————————————————————————————————————————
  393. //    Routine descriptors
  394. //——————————————————————————————————————————————————————————————————————————————
  395.  
  396. static RoutineDescriptor NewHandlePatchRD
  397.                 = BUILD_ROUTINE_DESCRIPTOR(uppNewHandleProcInfo, NewHandlePatch);
  398.  
  399. static RoutineDescriptor NewPtrPatchRD
  400.                 = BUILD_ROUTINE_DESCRIPTOR(uppNewPtrProcInfo, NewPtrPatch);
  401.  
  402. static RoutineDescriptor DisposePtrPatchRD
  403.                 = BUILD_ROUTINE_DESCRIPTOR(uppDisposePtrProcInfo, DisposePtrPatch);
  404.  
  405. static RoutineDescriptor GetPtrSizePatchRD
  406.                 = BUILD_ROUTINE_DESCRIPTOR(uppGetPtrSizeProcInfo, GetPtrSizePatch);
  407.  
  408. static RoutineDescriptor SetPtrSizePatchRD
  409.                 = BUILD_ROUTINE_DESCRIPTOR(uppSetPtrSizeProcInfo, SetPtrSizePatch);
  410.  
  411. static RoutineDescriptor MaxBlockPatchRD
  412.                 = BUILD_ROUTINE_DESCRIPTOR(uppMaxBlockProcInfo, MaxBlockPatch);
  413.  
  414. static RoutineDescriptor PtrZonePatchRD
  415.                 = BUILD_ROUTINE_DESCRIPTOR(uppPtrZoneProcInfo, PtrZonePatch);
  416.  
  417.  
  418. #if GENERATING68K
  419. /*
  420.     What the heck is this stuff? On 68K we have some headaches trying to patch
  421.     NewPtr, which returns two values: the pointer in A0 and an OSErr in D0.
  422.     MixedMode only allows one return value. To work around this on 68K, the
  423.     _real_ trap patch will be classic non-CFM 68K assembly which calls the CFM
  424.     patch (NewPtrPatch) through a RoutineDescriptor, then sets D0.
  425.     A gotcha here is that the value in the RoutineDescriptor is different for
  426.     different CFM contexts, since it points to a TVector. This makes sense,
  427.     since NewPtrPatch has to reference the global gOSHeap. To allow this,
  428.     we can't use normal code since it's shared. So we force the stub to be
  429.     generated in the global data space, which is per-context. Hence the hand
  430.     assembly below.
  431. */
  432.  
  433.     #if PRAGMA_ALIGN_SUPPORTED
  434.     #pragma options align=mac68k
  435.     #endif
  436.     
  437.     struct PatchWrapper {
  438.         short code1[1];
  439.         UniversalProcPtr upp;
  440.         short code2[4];
  441.     };
  442.     
  443.     static PatchWrapper gNewHandlePatch = {
  444.             {0x4EB9}, &NewHandlePatchRD,    // jsr        &NewHandlePatchRD
  445.             {0x3038, 0x0220,                // move.w    MemErr,D0
  446.              0x48C0,                        // ext.l    D0
  447.              0x4E75}                        // rts
  448.         };
  449.     static PatchWrapper gNewPtrPatch = {
  450.             {0x4EB9}, &NewPtrPatchRD,        // jsr        &NewPtrPatchRD
  451.             {0x3038, 0x0220,                // move.w    MemErr,D0
  452.              0x48C0,                        // ext.l    D0
  453.              0x4E75}                        // rts
  454.         };
  455.     static PatchWrapper gPtrZonePatch = {
  456.             {0x4EB9}, &PtrZonePatchRD,        // jsr        &PtrZonePatchRD
  457.             {0x3038, 0x0220,                // move.w    MemErr,D0
  458.              0x48C0,                        // ext.l    D0
  459.              0x4E75}                        // rts
  460.         };
  461.         
  462.     #if PRAGMA_ALIGN_SUPPORTED
  463.     #pragma options align=reset
  464.     #endif
  465. #endif
  466.  
  467.  
  468. //——————————————————————————————————————————————————————————————————————————————
  469. //    MMOverridePlatform
  470. //——————————————————————————————————————————————————————————————————————————————
  471. void
  472. MMOverridePlatform( MMBoolean ptrs, MMBoolean handles )
  473. {
  474.     if( ptrs ) {
  475.         gOverridePtrs++;
  476.         if( gNewPtrAddress == kMMNULL ) {
  477.             
  478.             // Install patches 1st time called:
  479.             #if GENERATINGPOWERPC
  480.                 OSErr err= FindEmulatorCall();
  481.                 if( err != noErr ) {
  482.                     MM_WARN("Couldn't find SetEmulatorRegister, err %d",err);
  483.                     return;
  484.                 }
  485.             #endif
  486.                 
  487.             gOSHeap = MMGetDefaultHeap();
  488.             
  489.             gNewPtrAddress        = GetOSTrapAddress(_NewPtr);
  490.             gPtrZoneAddress        = GetOSTrapAddress(_PtrZone);
  491.             gDisposePtrAddress    = GetOSTrapAddress(_DisposePtr);
  492.             gGetPtrSizeAddress    = GetOSTrapAddress(_GetPtrSize);
  493.             gSetPtrSizeAddress    = GetOSTrapAddress(_SetPtrSize);
  494. //            gMaxBlockAddress    = GetOSTrapAddress(_MaxBlock);
  495.  
  496.             #if GENERATING68K
  497.                 SetOSTrapAddress((UniversalProcPtr) &gNewPtrPatch,     _NewPtr);
  498.                 SetOSTrapAddress((UniversalProcPtr) &gPtrZonePatch,     _PtrZone);
  499.             #else
  500.                 SetOSTrapAddress((UniversalProcPtr) &NewPtrPatchRD,    _NewPtr);
  501.                 SetOSTrapAddress((UniversalProcPtr) &PtrZonePatchRD,   _PtrZone);
  502.             #endif
  503.             SetOSTrapAddress((UniversalProcPtr) &DisposePtrPatchRD, _DisposePtr);
  504.             SetOSTrapAddress((UniversalProcPtr) &GetPtrSizePatchRD, _GetPtrSize);
  505.             SetOSTrapAddress((UniversalProcPtr) &SetPtrSizePatchRD, _SetPtrSize);
  506.         //    SetOSTrapAddress((UniversalProcPtr) &MaxBlockPatchRD, _MaxBlock);
  507.  
  508.             #if GENERATING68K
  509.             #else
  510.             #endif
  511.         }
  512.     }
  513.     
  514.     if( handles ) {
  515.         gOverrideHandles++;
  516.         if( gNewHandleAddress == kMMNULL ) {
  517.             gNewHandleAddress = GetOSTrapAddress(_NewHandle);
  518.             #if GENERATING68K
  519.                 SetOSTrapAddress((UniversalProcPtr) &gNewHandlePatch, _NewHandle);
  520.             #else
  521.                 SetOSTrapAddress((UniversalProcPtr) &NewHandlePatchRD, _NewHandle);
  522.             #endif
  523.             
  524. /*            MM_WARN("Testing handles...");
  525.             Handle h = NewHandle(1234);
  526.             MM_WARN("Handle is at %p",h);
  527.             DisposeHandle(h);
  528.             MM_WARN("...Done testing handles.");*/
  529.         }
  530.     }
  531. }
  532.  
  533.  
  534. //——————————————————————————————————————————————————————————————————————————————
  535. //    MMEndOverridePlatform
  536. //——————————————————————————————————————————————————————————————————————————————
  537. void
  538. MMEndOverridePlatform( MMBoolean ptrs, MMBoolean handles )
  539. {
  540.     if( ptrs ) {
  541.         if( gOverridePtrs > 0 )
  542.             gOverridePtrs--;
  543.         else
  544.             MM_WARN("Too many MMEndOverridePlatform(true,...)");
  545.     }
  546.     if( handles ) {
  547.         if( gOverrideHandles > 0 )
  548.             gOverrideHandles--;
  549.         else
  550.             MM_WARN("Too many MMEndOverridePlatform(...,true)");
  551.     }
  552. }
  553.  
  554.  
  555. //——————————————————————————————————————————————————————————————————————————————
  556. //    MMOverridingPlatform
  557. //——————————————————————————————————————————————————————————————————————————————
  558. void
  559. MMOverridingPlatform( MMBoolean *ptrs, MMBoolean *handles )
  560. {
  561.     if( ptrs )        *ptrs    = (gOverridePtrs>0);
  562.     if( handles )    *handles = (gOverrideHandles>0);
  563. }
  564.